package com.ruoyi.erp.service.impl;

import java.math.BigDecimal;
import java.util.AbstractList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.erp.domain.*;
import com.ruoyi.erp.service.*;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.erp.mapper.StaffingMonthBillMapper;
import com.ruoyi.common.core.text.Convert;

/**
 * 外派月账单Service业务层处理
 * 
 * @author sauger
 * @date 2022-03-17
 */
@Service
public class StaffingMonthBillServiceImpl implements IStaffingMonthBillService 
{


    @Autowired
    private StaffingMonthBillMapper staffingMonthBillMapper;
    @Autowired
    private IProjectMemberService projectMemberService;
    @Autowired
    IStaffingSettingService staffingSettingService;
    @Autowired
    IHolidayConfigService holidayConfigService;
    @Autowired
    IPayrollService payrollService;


    @Override
    public void calculateMonthBillById(Integer id) {
        StaffingMonthBill bill = staffingMonthBillMapper.selectStaffingMonthBillById(id);
        StaffingSetting setting = staffingSettingService.selectStaffingSettingByResouce("person", bill.getPersonId());
        this.calculate(bill,setting);
        this.calculateProfitRate(bill);
    }

    /**
     * 根据外派人员产生应结算费用
     * @param projectMemberId  project_member 表中对应的ID
     */
    @Override
    public void calculateMonthByPerson(Integer projectMemberId, Integer year, Integer month) {

    }

    /**
     * 查询外派月账单
     * 
     * @param id 外派月账单主键
     * @return 外派月账单
     */
    @Override
    public StaffingMonthBill selectStaffingMonthBillById(Integer id)
    {
        return staffingMonthBillMapper.selectStaffingMonthBillById(id);
    }



    /**
     * 查询外派月账单列表
     * 
     * @param staffingMonthBill 外派月账单
     * @return 外派月账单
     */
    @Override
    public List<StaffingMonthBill> selectStaffingMonthBillList(StaffingMonthBill staffingMonthBill)
    {
        return staffingMonthBillMapper.selectStaffingMonthBillList(staffingMonthBill);
    }

    /**
     * 新增外派月账单
     * 
     * @param staffingMonthBill 外派月账单
     * @return 结果
     */
    @Override
    public int insertStaffingMonthBill(StaffingMonthBill staffingMonthBill)
    {
        return staffingMonthBillMapper.insertStaffingMonthBill(staffingMonthBill);
    }

    /**
     * 修改外派月账单
     * 
     * @param staffingMonthBill 外派月账单
     * @return 结果
     */
    @Override
    public int updateStaffingMonthBill(StaffingMonthBill staffingMonthBill)
    {
        staffingMonthBill.setUpdateTime(DateUtils.getNowDate());
        return staffingMonthBillMapper.updateStaffingMonthBill(staffingMonthBill);
    }

    /**
     * 批量删除外派月账单
     * 
     * @param ids 需要删除的外派月账单主键
     * @return 结果
     */
    @Override
    public int deleteStaffingMonthBillByIds(String ids)
    {
        return staffingMonthBillMapper.deleteStaffingMonthBillByIds(Convert.toStrArray(ids));
    }

    /**
     * 删除外派月账单信息
     * 
     * @param id 外派月账单主键
     * @return 结果
     */
    @Override
    public int deleteStaffingMonthBillById(Integer id)
    {
        return staffingMonthBillMapper.deleteStaffingMonthBillById(id);
    }

    /**
     * 根据项目id，批量更新月账单
     * @param year 需要产生的年
     * @param month 需要产生的月份
     * @param projectIds 需要产生的项目id
     * @param force 如果已经产生过，是否要强制产生并覆盖原有记录
     * @return 成功修改的记录条数
     */
    @Override
    public int batchCalculateBillByProjectId(Integer year, Integer month, List<Integer> projectIds, Boolean force) {
        return 0;
    }

    /**
     * 根据项目id，批量更新月账单
     * @param year 需要产生的年
     * @param month 需要产生的月份
     * @param customerIds 需要产生的客户id
     * @param force 如果已经产生过，是否要强制产生并覆盖原有记录
     * @return 成功修改的记录条数
     */
    @Override
    public int batchCalculateBillByCustomerId(Integer year, Integer month, List<Integer> customerIds, Boolean force) {
        return 0;
    }
    /**
     * 根据项目id，批量更新月账单
     * @param year 需要产生的年
     * @param month 需要产生的月份
     * @param personIds 需要产生的人员id
     * @param force 如果已经产生过，是否要强制产生并覆盖原有记录
     * @return 成功修改的记录条数
     */

    @Override
    public int batchCalculateBillByPersonId(Integer year, Integer month, List<Integer> personIds, Boolean force) {
        return 0;
    }

    /**
     *
     * @param year 需要产生的年
     * @param month 需要产生的月份
     * @param force 如果已经产生过，是否要强制产生并覆盖原有记录
     * @return 成功修改的记录条数
     */
    @Override
    public int batchCalculateBill(Integer year, Integer month, Boolean force) {
        return 0;
    }

    @Override
    public List<StaffingMonthBill> selectStaffingMonthBillByIds(String[] ids) {
        return staffingMonthBillMapper.selectStaffingMonthBillByIds(ids);
    }

    /**
     * 初始化产生某月的账单，如果已经产生的，则忽略，根据人员和当月的新增天数插入
     * @param year
     * @param month
     * @return
     */
    @Override
    public int initMonthBills(Integer year, Integer month)
    {
        List<ProjectMember> members = projectMemberService.selectProjectMemeberNeedInitMonthBill(year, month);
        if (StringUtils.isNull(members) || members.isEmpty()) {
            return 0;
        }
        List<StaffingMonthBill> bills = Lists.newArrayList();
        HashMap<Integer, Integer> workDays = holidayConfigService.getWorkdayCountByMonth(year, month);
        members.forEach(member->{
            int days = workDays.getOrDefault(member.getStaffingSetting().getHolidayConfigType(),0);
            //没找到对应的假期设置，就不使用假期
            if (days ==0){
                days = workDays.get(0);
            }

            StaffingMonthBill bill = new StaffingMonthBill();
            bill.setBillYear(year);
            bill.setBillMonth(month);
            bill.setPersonId(member.getId());
            bill.setProjectId(member.getProjectId());
            bill.setFullDayCount(BigDecimal.valueOf(days));


            //初始化其他字段为0
            bill.setNormalDayCount(BigDecimal.ZERO);
            bill.setAbsentDayCount(BigDecimal.ZERO);
            bill.setOvertimeFee(BigDecimal.ZERO);
            bill.setOvertimeHour1(BigDecimal.ZERO);
            bill.setOvertimeHour2(BigDecimal.ZERO);
            bill.setOvertimeHour3(BigDecimal.ZERO);
            bill.setOvertimeHour4(BigDecimal.ZERO);
            bill.setOtherFee(BigDecimal.ZERO);

            bill.setWorkFee(BigDecimal.ZERO);
            bill.setOvertimeFee(BigDecimal.ZERO);
            bill.setExpenseRateFee(BigDecimal.ZERO);
            bill.setExpenseFee(BigDecimal.ZERO);
            bill.setTotalFee(BigDecimal.ZERO);
            bill.setPayedFee(BigDecimal.ZERO);
            bill.setBilledFee(BigDecimal.ZERO);
            bills.add(bill);
        });

        staffingMonthBillMapper.batchInsertStaffingMonthBill(bills);

        return bills.size();
    }

    @Override
    public List<StaffingMonthBill> searchStaffingMonthBill(SearchMonthBill search) {
        return staffingMonthBillMapper.searchStaffingMonthBill(search);
    }

    private void calculate(StaffingMonthBill bill, StaffingSetting setting)
    {
        this.calculateWorkFee(bill, setting);
        this.calculateAbsentFee(bill,setting);
        this.calculateOverTimeFee(bill,setting);
        this.calculateTotal(bill,setting);
        this.calculateExpireDate(bill,setting);
        if (StringUtils.isNull(bill.getId())){
            this.insertStaffingMonthBill(bill);
        }else{
            this.updateStaffingMonthBill(bill);
        }
    }


    /**
     * 导入数据
     *
     * @param bills
     * @param operator  操作人
     * @param calculate 导入后，是否立即计算
     * @param update    如果存在，是否更新
     * @return
     */
    @Override
    public String importData(List<StaffingMonthBill> bills, String operator, boolean calculate, boolean update) {
        if (StringUtils.isNull(bills) || bills.size() == 0){
            throw new ServiceException("导入数据不能为空！");
        }
        int successNum = 0;
        int failureNum = 0;
        int skipNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        for (StaffingMonthBill bill: bills)
        {
            try
            {
                if(this.importOne(bill,operator,calculate,update) == 1){
                    successNum++;
                } else {
                    skipNum++;
                }
            }catch (Exception e)
            {
                failureNum++;
                String msg = "<br/>" + failureNum + "、项目 " + bill.getProjectName() + "中 " + bill.getPersonName() + "：" +bill.getBillYear() + "-" + bill.getBillMonth() + " 导入失败：";
                failureMsg.append(msg + e.getMessage());
            }
        }

        if (failureNum > 0)
        {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new ServiceException(failureMsg.toString());
        }
        else
        {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条");
            if (skipNum > 0) successMsg.append(" 跳过 " + skipNum + " 条");
        }
        return successMsg.toString();
    }

    /**
     * 计算月账单毛利率
     *
     * @param bill
     */
    @Override
    public void calculateProfitRate(StaffingMonthBill bill) {
        Payroll search = new Payroll();
        search.setYear(bill.getBillYear());
        search.setMonth(bill.getBillMonth());
        search.setUserId(bill.getUserId());
        List<Payroll> payrolls = payrollService.selectPayrollList(search);
        //没有成本数据，跳过
        if (StringUtils.isNull(payrolls) || payrolls.isEmpty()) return;
        Payroll payroll = payrolls.get(0);

        BigDecimal revenue = bill.getTotalFee().subtract(bill.getExpenseRateFee()).divide(BigDecimal.valueOf(1.06),4,BigDecimal.ROUND_HALF_UP);//去税收入
        BigDecimal cost = payroll.getTotalFee().subtract(payroll.getExpenseFee()); //成本
        bill.setProfitRate(revenue.subtract(cost).divide(revenue,4,BigDecimal.ROUND_HALF_UP));
        StringBuilder sb = new StringBuilder();
        sb.append("<p>去报销去税金额=(" + bill.getTotalFee().toString() + " - " + bill.getExpenseRateFee().toString() + ") /1.06 = " +revenue + "</p>");
        sb.append("<p>去报销成本金额=" + payroll.getTotalFee() + " - " + payroll.getExpenseFee().toString() + " = " +cost + "</p>");
        sb.append("<p>毛利="+"(" + revenue +" - "+ cost +")/" + revenue + "=" +bill.getProfitRate() +  "</p>");
        bill.setProfitExplain(sb.toString());
        this.updateStaffingMonthBill(bill);
    }

    /**
     * 处理一条数据
     * @param bill
     * @param operator
     * @param calculate
     * @param update
     */
    private int importOne(StaffingMonthBill bill, String operator, boolean calculate, boolean update)
    {
        StaffingMonthBill search = new StaffingMonthBill();
        search.setPersonId(bill.getPersonId());
        search.setProjectId(bill.getProjectId());
        search.setBillMonth(bill.getBillMonth());
        search.setBillYear(bill.getBillYear());
        bill.setUpdateBy(operator);
        bill.setUpdateTime(DateUtils.getNowDate());
        List<StaffingMonthBill> list = this.selectStaffingMonthBillList(search);
        //不存在，则插入
        if (StringUtils.isNull(list) || list.isEmpty())
        {
            this.insertStaffingMonthBill(bill);
        }else
        {
            if (!update) return 0;//存在，且不需要更新
            bill.setId(list.get(0).getId());
            this.updateStaffingMonthBill(bill);
        }
        if (calculate) this.calculateMonthBillById(bill.getId());
        return 1;
    }

    private  void calculateExpireDate(StaffingMonthBill bill, StaffingSetting setting)
    {
        if (StringUtils.isNull(setting.getBillExpireMonth()) || setting.getBillExpireMonth() <= 0 || StringUtils.isNull(setting.getBillType()))
        {
            bill.setExpireTime(null);
            return;
        }
        Integer addMonth = setting.getBillExpireMonth() + 1;
        //如果是季度结算
        if (setting.getBillType().equals("S")){
            int iTemp = bill.getBillMonth() % 3;
            switch (iTemp){
                case 0:
                    break;
                case 1:
                    addMonth += 2;
                    break;
                case 2:
                    addMonth++;
                default:

            }
        }

        Date expireDate = DateUtils.parseDate(bill.getBillYear() + "-" + bill.getBillMonth()+ "-1");
        expireDate = DateUtils.addMonths(expireDate, addMonth);
        bill.setExpireTime(expireDate);

    }

    private void calculateTotal(StaffingMonthBill bill, StaffingSetting setting)
    {
        //处理报销金额
        if (!StringUtils.isNull(bill.getExpenseFee()) && bill.getExpenseFee().compareTo(BigDecimal.ZERO) == 1){
            BigDecimal expenseFee = (StringUtils.isNull(setting.getExpenseRate()) ? BigDecimal.ZERO : setting.getExpenseRate().multiply(bill.getExpenseFee()).divide(BigDecimal.valueOf(100)));
            bill.setExpenseRateFee(expenseFee);
        }else {
            bill.setExpenseRateFee(BigDecimal.ZERO);
        }
        //其他费用处理
        if (!StringUtils.isNull(bill.getOtherFee()) && bill.getOtherFee().compareTo(BigDecimal.ZERO) == 1){
            bill.setOtherFee(bill.getOtherFee());
        }else {
            bill.setOtherFee(BigDecimal.ZERO);
        }
        bill.setTotalFee(bill.getWorkFee().add(bill.getExpenseRateFee()).add(bill.getOtherFee()).add(bill.getOvertimeFee()));
    }

    private  void calculateOverTimeFee(StaffingMonthBill bill, StaffingSetting setting)
    {
        BigDecimal price;
        if (setting.getQuotationType().equals("D")){
            price = setting.getQuotation();
        }else if (setting.getQuotationType().equals("H")){
            price = setting.getQuotation().multiply(BigDecimal.valueOf(8));
        }else{
            if(setting.getOvertimeType().equals("1"))
            {
                price = setting.getQuotation().divide(BigDecimal.valueOf(21.75),4,BigDecimal.ROUND_HALF_UP);
            }else{
                price = setting.getQuotation().divide(bill.getFullDayCount(),4,BigDecimal.ROUND_HALF_UP);
            }
        }

        BigDecimal fee = BigDecimal.ZERO;
        if (bill.getOvertimeHour1().compareTo(BigDecimal.ZERO) == 1) {
            fee = fee.add(price.multiply(bill.getOvertimeHour1().divide(BigDecimal.valueOf(8))));
        }
        if (bill.getOvertimeHour2().compareTo(BigDecimal.ZERO) == 1) {
            fee = fee.add(price.multiply(bill.getOvertimeHour2().divide(BigDecimal.valueOf(8))).multiply(BigDecimal.valueOf(1.5)));
        }
        if (bill.getOvertimeHour3().compareTo(BigDecimal.ZERO) == 1) {
            fee = fee.add(price.multiply(bill.getOvertimeHour3().divide(BigDecimal.valueOf(8))).multiply(BigDecimal.valueOf(2)));
        }
        if (bill.getOvertimeHour4().compareTo(BigDecimal.ZERO) == 1) {
            fee = fee.add(price.multiply(bill.getOvertimeHour4().divide(BigDecimal.valueOf(8))).multiply(BigDecimal.valueOf(3)));
        }
        bill.setOvertimeFee(fee);
    }

    private void calculateAbsentFee(StaffingMonthBill bill, StaffingSetting setting)
    {
        if (bill.getAbsentDayCount().equals(BigDecimal.ZERO)) return;
        //如果是按天或者按小时结算的，不处理缺勤
        if (setting.getQuotationType().equals("D") || setting.getQuotationType().equals("H")) return;
        BigDecimal price;
        switch (setting.getAbsentType())
        {
            case "fix": //月单价-月单价/21.75*缺勤天数
                price = setting.getQuotation().divide(BigDecimal.valueOf(21.75),4,BigDecimal.ROUND_HALF_UP);
                bill.setWorkFee(bill.getWorkFee().subtract(price.multiply(bill.getAbsentDayCount())));
                break;
            case "fix2": //月单价-月单价*0.046*缺勤天数
                price = setting.getQuotation().multiply(BigDecimal.valueOf(0.046));
                bill.setWorkFee(bill.getWorkFee().subtract(price.multiply(bill.getAbsentDayCount())));
                break;
            case "fix3": //月单价/21.75*实际出勤天数 不处理，在计算出勤天数时已处理

                break;
            case "percent":
                price = setting.getQuotation().divide(bill.getFullDayCount(),4,BigDecimal.ROUND_HALF_UP);
                bill.setWorkFee(bill.getWorkFee().subtract(price.multiply(bill.getAbsentDayCount())));
                break;
            case "ge":
                if (bill.getFullDayCount().compareTo(BigDecimal.valueOf(21.75)) == -1){
                    //小于21.75天
                    price = setting.getQuotation().divide(BigDecimal.valueOf(21.75),4,BigDecimal.ROUND_HALF_UP);
                }else
                {
                    price = setting.getQuotation().divide(bill.getFullDayCount(),4,BigDecimal.ROUND_HALF_UP);
                }
                bill.setWorkFee(bill.getWorkFee().subtract(price.multiply(bill.getAbsentDayCount())));
                break;
            default:
        }

    }

    private void calculateWorkFee(StaffingMonthBill bill, StaffingSetting setting)
    {
        //计算工时费用
        if (StringUtils.isNull(bill.getNormalDayCount()) || bill.getNormalDayCount().equals(BigDecimal.ZERO))
        {
            //无正常工时
            bill.setWorkFee(BigDecimal.ZERO);
            return;
        }
        switch (setting.getQuotationType())
        {
            case "D":
                bill.setWorkFee(setting.getQuotation().multiply(bill.getNormalDayCount()));
                break;
            case "H":
                bill.setWorkFee(setting.getQuotation().multiply(bill.getNormalDayCount().multiply(BigDecimal.valueOf(8))));
                break;
            case "M":
                if (bill.getNormalDayCount().equals(bill.getFullDayCount())){
                    //全勤
                    bill.setWorkFee(setting.getQuotation());
                    break;
                }
                //当请假扣款方式为fix3,月单价/21.75*实际出勤天数 时，工时费直接计算，其他情况下，先填入月报价，后续通过扣出缺勤部分
                if (setting.getAbsentType().equals("fix3"))
                {
                    bill.setWorkFee(setting.getQuotation().divide(BigDecimal.valueOf(21.75),4,BigDecimal.ROUND_HALF_UP).multiply(bill.getNormalDayCount()));
                }else
                {
                    bill.setWorkFee(setting.getQuotation());
                }

                break;
            default:
                bill.setWorkFee(BigDecimal.ZERO);
        }
    }
}
